#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2021/3/30 15:16
# @Author  : Grayson Liu
# @Email   : graysonliu@foxmail.com
# @File    : MethodWizardDialog.py

from PyQt5.Qt import *

import json

from ui.Ui_MethodWizardDialog import Ui_MethodWizardDialog
from MWDStep1 import MWDStep1
from MWDStep2 import MWDStep2
from MDWStep3 import MWDStep3
from CustomSignal import CustomSignal


class MethodWizardDialog(QDialog, Ui_MethodWizardDialog):
    """
    添加方法的对话框，包含方法向导步骤的3个子widget：MDWStep1，MDWStep2，MDWStep3
    """
    global_signal = CustomSignal()

    def __init__(self, parent=None):
        """
        初始化
        :param parent: 父窗体
        """
        super(MethodWizardDialog, self).__init__(parent)
        self.setupUi(self)

        # 添加成员变量
        self.method_type = 0  # 指示方法的类别
        self.method_index = 0  # 指示方法的序号
        self.parameter_list = []  # 指示选择方法的参数列表

        # 相关信息存储在method.json文件中，是字典
        self.method_data = json.load(open("config/method.json", "r", encoding="utf-8"))

        # 将MethodWizardDialog UI中 中心部分Widget设定为堆叠布局，根据选择的功能进行切换，显示不同的Widget
        self.replaceableWidget = QStackedLayout(self.wizardView)

        # TODO: 在这里实例化不同功能Widget的class，使用self.replaceableWidget.addWidget(...)添加到布局中
        self.step1 = MWDStep1()
        self.replaceableWidget.addWidget(self.step1)
        self.step2 = MWDStep2()
        self.replaceableWidget.addWidget(self.step2)
        self.step3 = MWDStep3()
        self.replaceableWidget.addWidget(self.step3)

        # 设定默认状态下的index
        self.replaceableWidget.setCurrentIndex(0)

        # 关联信号和槽，用于窗口之间的切换，需要添加更多的逻辑
        self.step1.btnNextStep.clicked.connect(self.select_method)
        self.step1.btnCancel.clicked.connect(lambda: self.close())
        self.step2.btnNextStep.clicked.connect(self.select_parameter)
        self.step2.btnPreviousStep.clicked.connect(self.back_select_method)
        self.step3.btnConfirm.clicked.connect(self.confirm_method)
        self.step3.btnPreviousStep.clicked.connect(self.back_select_parameter)

        # 为方法选择的list关联槽函数，当用户单击方法时，更改用于指示方法序号的成员变量，更改右侧信息栏的提示信息
        self.step1.listEnhancement.currentItemChanged.connect(self.change_method_item)
        self.step1.listDetection.currentItemChanged.connect(self.change_method_item)
        self.step1.listOptimization.currentItemChanged.connect(self.change_method_item)

        # 为方法选择的tab栏目关联槽函数，当切换tab时，更改用于指示方法类别的成员变量
        self.step1.tabWidget.currentChanged.connect(self.change_method_type)
        self.step1.tabWidget.currentChanged.connect(self.change_method_item)

        # 设置方法选择中，默认的tab栏目
        self.step1.tabWidget.setCurrentIndex(0)

    def change_method_type(self):
        """
        向导第1页，在tab栏目切换时触发的槽函数
        :return:
        """
        self.method_type = self.step1.tabWidget.currentIndex()  # 当前tab栏的序号用于指示方法的类别
        # 切换时，设置对应方法选择list默认选择项
        if self.method_type == 0:
            self.step1.listEnhancement.setCurrentRow(0)
        elif self.method_type == 1:
            self.step1.listDetection.setCurrentRow(0)
        else:
            self.step1.listOptimization.setCurrentRow(0)

    def change_method_item(self):
        """
        向导第1页，在list中鼠标单机选择方法时触发的槽函数，会更改成员变量method_type和method_index，同时改变右侧信息栏的提示信息
        :return:
        """
        if self.method_type == 0:
            self.method_index = self.step1.listEnhancement.currentIndex().row()
        elif self.method_type == 1:
            self.method_index = self.step1.listDetection.currentIndex().row()
        else:
            self.method_index = self.step1.listOptimization.currentIndex().row()

        # 在右侧信息栏显示提示信息
        parameter_str = ""
        parameter_num = self.method_data[self.method_type][self.method_index]["parameter_number"]
        if parameter_num == 0:
            parameter_str += "无参数"
        else:
            for i in range(parameter_num):
                parameter_str += \
                    "参数{}：\n【类型：{}】  {}\n".format(i + 1,
                        self.method_data[self.method_type][self.method_index]["parameters"][i]["type"],
                        self.method_data[self.method_type][self.method_index]["parameters"][i]["name"])

        self.step1.textMethodDetail.setText("方法类别：{}\n方法名称：{}\n方法介绍：{}\n\n{}".format(
            self.method_data[self.method_type][self.method_index]["type"],
            self.method_data[self.method_type][self.method_index]["name"],
            self.method_data[self.method_type][self.method_index]["description"],
            parameter_str))

    def select_method(self):
        """
        向导第1页，点击“下一步”按钮的槽函数，用于确定方法和切换到下一个界面
        :return:
        """
        self.setWindowTitle("添加方法向导-步骤2")
        self.replaceableWidget.setCurrentIndex(1)
        # 将该方法对应的参数显示在表格中
        # 设置表头的宽度
        self.step2.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        # 设置表格的行数
        row = self.method_data[self.method_type][self.method_index]["parameter_number"]
        if row != 0:
            self.step2.tableWidget.setRowCount(row)
            # 如果有参数，添加表格每一行的数据
            for i, parameter in enumerate(self.method_data[self.method_type][self.method_index]["parameters"]):
                self.__add_table_row(i, parameter)

        # 设置表格的可编辑性
        class EmptyDelegate(QItemDelegate):
            def __init__(self, parent):
                super(EmptyDelegate, self).__init__(parent)

            def createEditor(self, QWidget, QStyleOptionViewItem, QModelIndex):
                return None
        self.step2.tableWidget.setItemDelegateForColumn(0, EmptyDelegate(self))  # 设置第0列的不可编辑性
        self.step2.tableWidget.setItemDelegateForColumn(2, EmptyDelegate(self))  # 设置第2列的不可编辑性

    def __add_table_row(self, index: int, parameter: dict):
        """
        私有方法，在表格控件中添加一行
        :param parameter: 一个参数字典
        :return:
        """
        item_name = QTableWidgetItem(parameter["name"])  # 参数名
        item_value = QTableWidgetItem(str(parameter["default"]))  # 参数默认取值
        str_constraints = "{}, max: {}, min: {}, {}".format(parameter["type"],
                                                            parameter["max"],
                                                            parameter["min"],
                                                            parameter["constraints"])
        item_constraints = QTableWidgetItem(str_constraints)
        self.step2.tableWidget.setItem(index, 0, item_name)
        self.step2.tableWidget.setItem(index, 1, item_value)
        self.step2.tableWidget.setItem(index, 2, item_constraints)

    def select_parameter(self):
        """
        向导第2页，点击“下一步”按钮的槽函数，用于确定方法的参数和切换到下一个界面
        :return:
        """
        # 获取第2页用户设定的参数
        self.parameter_list.clear()
        self.step3.textBrowser.setText("")  # 先清空
        self.step3.textBrowser.append(
            "添加方法小结：\n{}  {}\n参数列表：".format(self.method_data[self.method_type][self.method_index]["type"],
                                            self.method_data[self.method_type][self.method_index]["name"]))
        # 设置表格的行数
        row = self.method_data[self.method_type][self.method_index]["parameter_number"]
        if row != 0:
            for i, parameter in enumerate(self.method_data[self.method_type][self.method_index]["parameters"]):
                value = eval(self.step2.tableWidget.item(i, 1).text())  # 用户输入的值：使用eval进行类型转换
                if not self.__validate_parameter(value, parameter):
                    QMessageBox.warning(self.step2, "参数错误", "参数不符合限定要求，请检查。", QMessageBox.Ok)
                    return
                self.step3.textBrowser.append("参数{}  {} = {}".format(i + 1, parameter["name"], value))
                self.parameter_list.append({"name": parameter["name"], "value": value})
        else:
            self.step3.textBrowser.append("该方法无参数")
        # 切换到下一个窗体
        self.setWindowTitle("添加方法向导-步骤3")
        self.replaceableWidget.setCurrentIndex(2)

    def __validate_parameter(self, value, parameter):
        """
        私有方法，用于检验参数是否符合限定要求
        :param value: 用户输入的参数值
        :param parameter: 描述该参数的字典
        :return: True或False
        """
        if parameter["type"] != type(value).__name__:
            return False
        if value < parameter["min"] or value > parameter["max"]:
            return False
        if parameter["constraints"] == "odd":
            if value % 2 != 1:
                return False
        return True

    def back_select_method(self):
        """
        向导第2页，点击“上一步”按钮的槽函数，用于返回上一个界面
        :return:
        """
        self.setWindowTitle("添加方法向导-步骤1")
        self.replaceableWidget.setCurrentIndex(0)

    def confirm_method(self):
        """
        向导第3页，点击“确认”按钮的槽函数，确认后，给ProjectForm发送信号
        :return:
        """
        method_name = self.method_data[self.method_type][self.method_index]["name"]
        MethodWizardDialog.global_signal.add_method.emit(method_name,
                                                         self.method_type,
                                                         self.method_index,
                                                         self.parameter_list)  # 发送信号
        self.close()

    def back_select_parameter(self):
        """
        向导第3页，点击“上一步”按钮的槽函数，用于返回上一个界面
        :return:
        """
        self.setWindowTitle("添加方法向导-步骤2")
        self.replaceableWidget.setCurrentIndex(1)

